package org.seamon.management.service.impl;

import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.seamon.management.pojo.dto.LoginDTO;
import org.seamon.management.pojo.vo.TokenVO;
import org.seamon.management.service.UserService;
import org.seamon.management.util.RedissonUtil;
import org.seamon.management.util.security.JwtUtil;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import static org.seamon.management.util.CacheConstants.BLACKLIST_TOKEN_PREFIX;
import static org.seamon.management.util.CacheConstants.USER_PERMS_PREFIX;

/**
 * @author Simon
 */
@Service
@RequiredArgsConstructor
public class AuthService {

    @Resource
    private RedissonUtil redissonUtil;
    @Resource
    private JwtUtil jwtUtil;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private UserService userService;

    /**
     * 登录
     *
     * @param loginDTO 登录信息
     * @return 登录成功后的 Token
     */
    public TokenVO login(LoginDTO loginDTO) {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(loginDTO.getUsername(), loginDTO.getPassword())
        );

        // 登录成功后，从 Authentication 中提取权限信息
        UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) authentication;
        UserDetails userDetails = (UserDetails) authenticationToken.getPrincipal();
        List<String> permissions = userDetails.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList());
        redissonUtil.setWithExpire(USER_PERMS_PREFIX + userDetails.getUsername(), permissions, Duration.ofHours(24));

        userService.updateLoginTime(loginDTO.getUsername());

        String token = jwtUtil.generateToken(loginDTO.getUsername(), userDetails.getAuthorities());
        String refreshToken = jwtUtil.generateToken(loginDTO.getUsername() + "-refresh", userDetails.getAuthorities());

        // 将权限信息缓存到 SecurityContextHolder
        UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(
                loginDTO.getUsername(),
                null,
                authenticationToken.getAuthorities()
        );
        SecurityContextHolder.getContext().setAuthentication(newAuth);

        TokenVO tokenVO = new TokenVO();
        tokenVO.setToken(token);
        tokenVO.setRefreshToken(refreshToken);
        tokenVO.setExpireIn(3600);

        return tokenVO;
    }

    /**
     * 登出
     * @param token 登出的 Token
     */
    public void logout(String token) {
        token = token.substring(7).trim();
        String username = jwtUtil.extractUsername(token);
        long expiration = jwtUtil.getExpiration(token).getTime() - System.currentTimeMillis();
        // 加入黑名单，并设置与 Token 剩余时间一致的 TTL
        redissonUtil.setWithExpire(BLACKLIST_TOKEN_PREFIX + token, username, Duration.ofMillis(expiration));
    }

    /**
     * 刷新 Token
     *
     * @param oldRefreshToken 旧的 Refresh Token
     * @return 新的 TokenVO，包含新的 Access Token 和 Refresh Token
     */
    public TokenVO refreshToken(String oldRefreshToken) {
        // 1. 校验 Refresh Token 是否有效
        if (!jwtUtil.validateToken(oldRefreshToken)) {
            throw new RuntimeException("Refresh Token 无效");
        }

        // 2. 从 Refresh Token 中提取用户名
        String username = jwtUtil.extractUsername(oldRefreshToken);

        // 3. 从 Redis 获取用户权限
        List<String> perms = (List<String>) redissonUtil.get(USER_PERMS_PREFIX + username);
        if (perms == null || perms.isEmpty()) {
            throw new RuntimeException("权限信息不存在");
        }

        Collection<? extends GrantedAuthority> authorities = perms.stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());

        // 4. 生成新的 Access Token
        String newAccessToken = jwtUtil.generateToken(username, authorities);

        // 5. 生成新的 Refresh Token（可选：重新生成）
        String newRefreshToken = jwtUtil.generateToken(username + "-refresh", authorities);

        // 6. 可选：将旧 Refresh Token 加入黑名单（防止重复使用）
        long expiration = jwtUtil.getExpiration(oldRefreshToken).getTime() - System.currentTimeMillis();
        redissonUtil.setWithExpire(BLACKLIST_TOKEN_PREFIX + oldRefreshToken, username, Duration.ofMillis(expiration));

        // 7. 更新 Redis 中的权限缓存（延长有效期）
        redissonUtil.setWithExpire(USER_PERMS_PREFIX + username, perms, Duration.ofHours(24));

        // 8. 返回新的 TokenVO
        TokenVO tokenVO = new TokenVO();
        tokenVO.setToken(newAccessToken);
        tokenVO.setRefreshToken(newRefreshToken);
        tokenVO.setExpireIn(3600); // 新 Access Token 的过期时间（秒）

        return tokenVO;
    }

}
